from exceptions import ImportError, ValueError

from models import *
from django.dispatch import Signal
from local_settings import REPUTATION_AGGREGATORS

event_registered = Signal(providing_args=["","",""])
verify_aggregators(REPUTATION_AGGREGATORS)


def get_dimension_by_name(name):
    dimensions = ReputationDimension.objects.filter(name=dimension)
    if reputations.count() == 1:
        dimension = dimensions[0]
    elif reputations.count() == 0:
        dimension = ReputationDemension(name=dimension)
        dimension.save()
    else:
        raise ValueError("More than one dimension record found with the same name.")
    return dimension


def register_event(event_score, user, dimension, related_obj=None, initiator=None
                   calculator=labmda x, user, related_obj, initiator, dimension: x,
                   aggregator=lambda x, user, related_obj, initiator, aggregation: x)
    '''
    When an event is registered, it is possible to supply function callbacks that are used
    to calculate the improvment in the single-dimension score (i.e. calculator), as well
    as to calculate the improvement in the aggregate score (i.e. aggregator).
   
    If neiter of these are specified, which is the usual case, 
    '''

    assert isinstance(dimension, ReputationDimension)

    reputations = ReputationScore.objects.filter(user=user, dimension=dimension)
    if reputations.count() == 1:
        reputation = reputations[0]
        aggregation = reputation.aggregation
 
        # Just make sure that the aggregation was assigned properly...
        assert aggregation.user == user and aggregation.dimension == None

    elif reputations.count() == 0:
        aggregations = ReputationScore.objects.filter(user=user, dimension=None, 
                                                      aggregation=None)
        if aggregations.count() == 1:
            aggregation = aggregations[0]
        elif aggregations.count() == 0:
            aggregation = ReputationScore(user=user, dimension=None)
        else:
            raise ValueError("More than one aggregating reputation found for user.")

        reputation = ReputationScore(user=user, dimension=dimension, aggregation=aggregation)
    else:
        raise ValueError("More than one reputation found for user and dimension.")

    score_delta  = calculator(event_score, user, related_obj, initiator, dimension)
    aggreg_delta = aggregator(event_score, user, related_obj, initiator, aggregation)

    reputation.score += score_delta
    reputation.save()
    aggregation.score += aggreg_delta
    aggregation.save()

    initiator = user if initiator is None else initiator
    reputation_event = ReputationEvent(user=user, score=reputation, score_delta=score_delta,
                                       aggregation=aggregation, aggregation_delta=aggreg_delta,
                                       dimension=dimension, related_obj=related_obj, 
                                       initiator=initiator)
    reputation_event.save()


def get_user_score(user, dimension=None):
    assert isinstance(dimension, ReputationDimension)

    reputations = ReputationScore.objects.filter(user=user, dimension=dimension)
    if reputations.count() == 1:
        reputation = reputations[0]
        return reputation
    elif reputations.count() == 0:
        return None
    else:
        raise ValueError("More than one reputation found for user and aspect.")
        

